/* *
 * --ライセンスについて--
 *
 * 「本ファイルの内容は Mozilla Public License Version 1.1 (「本ライセンス」)
 * の適用を受けます。
 * 本ライセンスに従わない限り本ファイルを使用することはできません。
 * 本ライセンスのコピーは http://www.mozilla.org/MPL/ から入手できます。
 *
 * 本ライセンスに基づき配布されるソフトウェアは、「現状のまま」で配布されるものであり、
 * 明示的か黙示的かを問わず、いかなる種類の保証も行われません。
 * 本ライセンス上の権利および制限を定める具体的な文言は、本ライセンスを参照してください。
 *
 * オリジナルコードおよび初期開発者は、N_H (h.10x64@gmail.com) です。
 *
 * N_H によって作成された部分の著作権表示は次のとおりです。
 *
 * Copyright (C) 2011 - 2012
 *
 * このファイルの内容は、上記に代えて、
 * GNU General License version2 以降 (以下 GPL とする)、
 * GNU Lesser General Public License Version 2.1 以降 (以下 LGPL とする)、
 * の条件に従って使用することも可能です。
 * この場合、このファイルの使用には上記の条項ではなく GPL または LGPL の条項が適用されます。
 * このファイルの他者による使用を GPL または LGPL の条件によってのみ許可し、
 * MPL による使用を許可したくない対象者は、上記の条項を削除することでその意思を示し、
 * 上記条項を GPL または LGPL で義務付けられている告知およびその他の条項に置き換えてください。
 * 対象者が上記の条項を削除しない場合、
 * 受領者は MPL または GPL または LGPL ライセンスのいずれによってもこのファイルを
 * 使用することができます。」
 *
 * -- License --
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License。You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND、either express or implied。See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is
 *   N_H (h.10x64@gmail.com).
 *
 * Portions created by the Initial Developer are Copyright (C) 2011 - 2012
 * the Initial Developer。All Rights Reserved.
 *
 * Alternatively、the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL")、or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above。If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL、and not to allow others to
 * use your version of this file under the terms of the MPL、indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL。If you do not delete
 * the provisions above、a recipient may use your version of this file under
 * the terms of any one of the MPL、the GPL or the LGPL.
 *
 * */
package com.magiciansforest.audio.vst.binaural;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 *
 * @author N_H <h.10x64@gmail.com>
 */
public class SoundPositionControlPanel extends JPanel implements ChangeListener, ActionListener {

    private static final String IMG_SIDE = "com/magiciansforest/audio/vst/binaural/side.png";
    private static final String IMG_TOP = "com/magiciansforest/audio/vst/binaural/top.png";
    private static final int MAX_DISTANCE = 5000;
    private Binaural parent;
    private SoundEnvironment env;
    private GridBagLayout layout = new GridBagLayout();
    private Grid xzDisp, yzDisp;
    private Knob elevKnob, azimKnob;
    private JSlider distSlider;
    private JLabel distLabel;
    private JSlider volumeSlider;
    private JLabel volumeLabel;
    private List<ChangeListener> changeListeners;
    private boolean ignoreChange = false;

    public SoundPositionControlPanel(Binaural parent, SoundEnvironment env) {
        super();
        super.setBackground(Color.black);

        this.parent = parent;
        this.env = env;

        changeListeners = new ArrayList<ChangeListener>();

        super.setLayout(layout);

        initialize();
        env.addChangeListener(this);
        
        env.setSourcePosition(0, 0, 1.0);
        distSlider.setValue(1000);
    }

    public void stateChanged(ChangeEvent e) {
        boolean isChanged = false;

        if (!ignoreChange) {
            if (e.getSource().equals(env) && !ignoreChange) {
                //Update Knob & Slider position
                Spherical s = env.getSourcePosition().toSpherical();
                ignoreChange = true;
                elevKnob.setRadian(s.elev + Math.PI / 4);
                ignoreChange = true;
                azimKnob.setRadian(s.azim);
                ignoreChange = true;
                distSlider.setValue((int) Math.round(s.dist * 1000));
            } else if (e.getSource().equals(elevKnob)) {
                Spherical pos = env.getSourcePosition().toSpherical();
                pos.elev = elevKnob.getRadian() - Math.PI / 4;
                ignoreChange = true;
                env.setSourcePosition(pos);
                isChanged = true;
            } else if (e.getSource().equals(azimKnob)) {
                Spherical pos = env.getSourcePosition().toSpherical();
                pos.azim = azimKnob.getRadian();
                ignoreChange = true;
                env.setSourcePosition(pos);
                isChanged = true;
            } else if (e.getSource().equals(distSlider)) {
                Spherical pos = env.getSourcePosition().toSpherical();
                pos.dist = (double) distSlider.getValue() / 1000;
                ignoreChange = true;
                env.setSourcePosition(pos);
                isChanged = true;
            } else if (e.getSource().equals(volumeSlider)) {
                parent.setVolume((float) volumeSlider.getValue() / 10);
            }
        } else {
            ignoreChange = false;
        }

        if (isChanged) {
            fireChangeEvent(new ChangeEvent(this));
        }
    }

    public void actionPerformed(ActionEvent e) {
    }

    public boolean addChangeListener(ChangeListener listener) {
        return changeListeners.add(listener);
    }

    public boolean removeChangeListener(ChangeListener listener) {
        return changeListeners.remove(listener);
    }

    private void initialize() {
        yzDisp = new Grid(env, Grid.YZ, 5);

        xzDisp = new Grid(env, Grid.XZ, 5);

        BufferedImage side;
        try {
            side = ImageIO.read(ClassLoader.getSystemResource(IMG_SIDE));
        } catch (IOException ioe) {
            ioe.printStackTrace();
            side = null;
        }
        elevKnob = new Knob(45, 0, 135, 45, 15, side, null, null);
        elevKnob.addChangeListener(this);

        BufferedImage top;
        try {
            top = ImageIO.read(ClassLoader.getSystemResource(IMG_TOP));
        } catch (IOException ioe) {
            ioe.printStackTrace();
            top = null;
        }
        azimKnob = new Knob(0, 0, 360, 180, 32, top, null, null);
        azimKnob.addChangeListener(this);

        distLabel = new JLabel("Dist.");
        distSlider = new JSlider(1, MAX_DISTANCE);
        distSlider.setBackground(super.getBackground());
        distSlider.addChangeListener(this);

        volumeLabel = new JLabel("Vol.");
        volumeSlider = new JSlider(0, 1200);
        volumeSlider.setBackground(Color.BLACK);
        volumeSlider.setValue(0);
        volumeSlider.addChangeListener(this);
        
        GridBagLayout gbLayout = new GridBagLayout();
        GridBagConstraints distLabelConstraints = new GridBagConstraints(0, 0, 1, 1, 1.0/6, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0);
        GridBagConstraints distSliderConstraints = new GridBagConstraints(1, 0, 1, 1, 5.0/6, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0);
        gbLayout.addLayoutComponent(distLabel, distLabelConstraints);
        gbLayout.addLayoutComponent(distSlider, distSliderConstraints);
        JPanel sliderPanel = new JPanel(gbLayout);
        sliderPanel.add(distLabel);
        sliderPanel.add(distSlider);
        sliderPanel.setBackground(Color.BLACK);

        addComponent(xzDisp, 0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, new Insets(5, 5, 5, 5));
        addComponent(yzDisp, 1, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, new Insets(5, 5, 5, 5));
        addComponent(azimKnob, 0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, new Insets(5, 5, 5, 5));
        addComponent(elevKnob, 1, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, new Insets(5, 5, 5, 5));
        addComponent(sliderPanel, 0, 2, 2, 1, 1, 0.125, GridBagConstraints.FIRST_LINE_START, new Insets(5, 5, 5, 5));
    }

    private void fireChangeEvent(ChangeEvent e) {
        Iterator<ChangeListener> itr = changeListeners.iterator();
        while (itr.hasNext()) {
            ChangeListener listener = itr.next();
            listener.stateChanged(e);
        }
    }

    private void addComponent(Component c, int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, int anchor, Insets insets) {
        GridBagConstraints constraint = new GridBagConstraints(
                gridx, gridy,
                gridwidth, gridheight,
                weightx, weighty,
                anchor, GridBagConstraints.BOTH,
                insets, 0, 0);
        layout.addLayoutComponent(c, constraint);
        add(c);
    }
}
